package com.aegean.socket.base;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.util.Set;
import java.util.logging.Logger;

public class TradionalSocketTCPNIOClient extends Thread {
	private SocketChannel sc = null;
	private Selector selector = null;
	private SelectionKey skey = null;
	private Handler handler = new clientHandler();
	private CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
	private CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
	private final String writeMsg = null;
	private static final Logger log = Logger
			.getLogger(TradionalSocketTCPNIOClient.class.getName());
	private final int pocketAddress = 8888;
	private final InetSocketAddress inetAD = new InetSocketAddress(
			pocketAddress);

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			if (init() == true) {
				//for(int i=0;i<10;i++)
				while(true){
					operation();
				}

			} else {
				System.out.println("The initialization of Client is false!");
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}

	private boolean init() {
		try {
			sc = SocketChannel.open();
			sc.configureBlocking(false);// 不进行赌赛
			sc.connect(inetAD);
			selector = Selector.open();
			skey = sc.register(selector, SelectionKey.OP_CONNECT);// 准备连接服务器就绪
			return true;
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * FunName sendMessage
	 * 
	 * @param msg
	 *            传入需要写事件的消息信息
	 * @return true 成功 false 不成功
	 * @throws Exception
	 */
	protected boolean sendMessage(String msg) throws Exception {
		SocketChannel channel = (SocketChannel) skey.channel();
		if (sc.isOpen() == true) {
			try {
				int writtedSize = 0;
				if (msg == null || msg.equals("")) {
					log.info("writed message is empty!");
				} else {
					try {
						writtedSize = channel.write(ByteBuffer.wrap(msg.getBytes("UTF-8")));
						if (writtedSize != 0) {
							System.out.println("注册写事件!");
							System.out.println("open:"+channel.isOpen()+" connected:"+channel.isConnected()+" connectionPending:" +channel.isConnectionPending()+" register:"+channel.isRegistered());
							channel.register(selector, SelectionKey.OP_WRITE);
						}
					} catch (Exception e) {
						// TODO: handle exception
						log.info("The sending message is countering the error!");
						e.printStackTrace();
						return false;
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
				return false;
			} finally {
				channel.close();
			}
		} else {
			return false;
		}
		return true;
	}

	private boolean operation() {
		try {
			int nkeys = selector.select();
			System.out.println("The total count of client Optimate Selector:"
					+ nkeys);
			if (nkeys > 0) {
				Set<SelectionKey> keys = selector.selectedKeys();// 获取被注册的channel
				for (SelectionKey key : keys) {
					if (key.isConnectable()) {
						System.out.println("Client connection is open");
						handler.NIOConnection(key);
					} else if (key.isReadable()) {
						System.out.println("Client reader is open");
						handler.NIOReader(key);
					} else if (key.isWritable()) {
						System.out.println("Client writter is open");
						handler.NIOWritter(key);
					}
					
				}
				selector.selectedKeys().clear();
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			return false;
		}
		return true;
	}

	interface Handler {
		void NIOReader(SelectionKey key);

		void NIOWritter(SelectionKey key);

		void NIOConnection(SelectionKey key);
	}

	class clientHandler implements Handler {

		@Override
		public void NIOReader(SelectionKey key) {
			// TODO Auto-generated method stub
			ByteBuffer buffer = ByteBuffer.allocate(1024);
			SocketChannel channel = (SocketChannel) key.channel();
			try {
				System.out.println("收到服务器消息："
						+ channel.socket().getInetAddress().getHostName()
						+ channel.socket().getInetAddress().getHostAddress()
						+ ":");
				while (true) {
					int readBytes = channel.read(buffer);
					if (readBytes > 0) {
						log.info("Server: readBytes = " + readBytes);
						log.info("Server: data = "
								+ new String(buffer.array(), 0, readBytes));
						buffer.flip();
					} else
						break;
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				if (buffer != null)
					buffer.clear();
				try {
					channel.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

		@Override
		public void NIOWritter(SelectionKey key) {
			// TODO Auto-generated method stub
			System.out.println("client niowritter!!!!!!!!!");
			ByteBuffer writeBuffer = (ByteBuffer) key.attachment();
			key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
			SocketChannel channel = (SocketChannel) key.channel();
			int writtedSize = 0;
			if (writeMsg == null || writeMsg.equals("")) {
				log.info("writed message is empty!");
			} else {
				try {
					writtedSize = channel.write(encoder.encode(CharBuffer
							.wrap(writeBuffer.asCharBuffer().array())));
					if (writtedSize == 0) {
						channel.register(selector, SelectionKey.OP_WRITE);
					}
				} catch (Exception e) {
					// TODO: handle exception
					log.info("The sending message is countering the error!");
					e.printStackTrace();
				}finally{
					try {
						channel.register(selector, SelectionKey.OP_READ);
						channel.close();
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}

		@Override
		public void NIOConnection(SelectionKey key) {
			SocketChannel channel = (SocketChannel) key.channel();
			// TODO Auto-generated method stub
			try {
				channel.configureBlocking(false);
				if (channel.isConnectionPending()) {
					System.out.println("client finish Connection");
					channel.finishConnect();
				}
				channel.register(selector, SelectionKey.OP_READ);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

}
